www.gusucode.com > Piwik 网站流量统计系统 v2.9.1PHP源码程序 > Piwik 网站流量统计系统 v2.9.1/piwik/piwik/plugins/SegmentEditor/javascripts/Segmentation.js
/*! * Piwik - free/libre analytics platform * * @link http://piwik.org * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later */ Segmentation = (function($) { function preselectFirstMetricMatch(rowNode) { var matchValue = $(rowNode).find('.metricMatchBlock option:first').attr('value'); $(rowNode).find('.metricMatchBlock select').val(matchValue); } var segmentation = function segmentation(config) { if (!config.target) { throw new Error("target property must be set in config to segment editor control element"); } var self = this; self.currentSegmentStr = ""; self.segmentAccess = "read"; self.availableSegments = []; for (var item in config) { self[item] = config[item]; } self.editorTemplate = self.editorTemplate.detach(); self.timer = ""; // variable for further use in timing events self.searchAllowed = true; self.filterTimer = ""; self.filterAllowed = true; self.availableMatches = []; self.availableMatches["metric"] = []; self.availableMatches["metric"]["=="] = self.translations['General_OperationEquals']; self.availableMatches["metric"]["!="] = self.translations['General_OperationNotEquals']; self.availableMatches["metric"]["<="] = self.translations['General_OperationAtMost']; self.availableMatches["metric"][">="] = self.translations['General_OperationAtLeast']; self.availableMatches["metric"]["<"] = self.translations['General_OperationLessThan']; self.availableMatches["metric"][">"] = self.translations['General_OperationGreaterThan']; self.availableMatches["dimension"] = []; self.availableMatches["dimension"]["=="] = self.translations['General_OperationIs']; self.availableMatches["dimension"]["!="] = self.translations['General_OperationIsNot']; self.availableMatches["dimension"]["=@"] = self.translations['General_OperationContains']; self.availableMatches["dimension"]["!@"] = self.translations['General_OperationDoesNotContain']; segmentation.prototype.setAvailableSegments = function (segments) { this.availableSegments = segments; }; segmentation.prototype.getSegment = function(){ var self = this; if($.browser.mozilla) { return self.currentSegmentStr; } return decodeURIComponent(self.currentSegmentStr); }; segmentation.prototype.setSegment = function(segmentStr){ if(!$.browser.mozilla) { segmentStr = encodeURIComponent(segmentStr); } this.currentSegmentStr = segmentStr; }; segmentation.prototype.shortenSegmentName = function(name, length){ if(typeof length === "undefined") length = 18; if(typeof name === "undefined") name = ""; var i; if(name.length > length) { return name.slice(0, length).trim() + "..."; } return name; }; segmentation.prototype.markCurrentSegment = function(){ var current = this.getSegment(); var segmentationTitle = $(this.content).find(".segmentationTitle"); if( current != "") { var currentDecoded = piwikHelper.htmlDecode(current); var selector = 'div.segmentList ul li[data-definition="'+currentDecoded+'"]'; var foundItems = $(selector, this.target); var title = $('<strong></strong>'); if( foundItems.length > 0) { var name = $(foundItems).first().find("span.segname").text(); title.text(name); } else { title.text("Custom Segment"); } segmentationTitle.html(title); } else { $(this.content).find(".segmentationTitle").text(this.translations['SegmentEditor_DefaultAllVisits']); } }; var getAndDiv = function(){ if(typeof andDiv === "undefined"){ var andDiv = self.editorTemplate.find("> div.segment-and").clone(); } return andDiv.clone(); }; var getOrDiv = function(){ if(typeof orDiv === "undefined"){ var orDiv = self.editorTemplate.find("> div.segment-or").clone(); } return orDiv.clone(); }; var getMockedInputSet = function(){ var mockedInputSet = self.editorTemplate.find("div.segment-row-inputs").clone(); var clonedInput = mockedInputSet.clone(); preselectFirstMetricMatch(clonedInput); return clonedInput; }; var getMockedInputRowHtml = function(){ var mockedInputRow = '<div class="segment-row"><a class="segment-close" href="#"></a><div class="segment-row-inputs">'+getMockedInputSet().html()+'</div></div>'; return mockedInputRow; }; var getMockedFormRow = function(){ var mockedFormRow = self.editorTemplate.find("div.segment-rows").clone(); $(mockedFormRow).find(".segment-row").append(getMockedInputSet()).after(getAddOrBlockButtonHtml).after(getOrDiv()); var clonedRow = mockedFormRow.clone(); preselectFirstMetricMatch(clonedRow); return clonedRow; }; var getInitialStateRowsHtml = function(){ if(typeof initialStateRows === "undefined"){ var content = self.editorTemplate.find("div.initial-state-rows").html(); var initialStateRows = $(content).clone(); } return initialStateRows; }; var revokeInitialStateRows = function(){ $(self.form).find(".segment-add-row").remove(); $(self.form).find(".segment-and").remove(); }; var appendSpecifiedRowHtml= function(metric) { var mockedRow = getMockedFormRow(); $(self.form).find(".segment-content > h3").after(mockedRow); $(self.form).find(".segment-content").append(getAndDiv()); $(self.form).find(".segment-content").append(getAddNewBlockButtonHtml()); doDragDropBindings(); $(self.form).find(".metricList").val(metric).trigger("change"); preselectFirstMetricMatch(mockedRow); }; var appendComplexRowHtml = function(block){ var key; var newRow = getMockedFormRow(); var x = $(newRow).find(".metricMatchBlock select"); $(newRow).find(".metricListBlock select").val(block[0].metric); $(newRow).find(".metricMatchBlock select").val(block[0].match); $(newRow).find(".metricValueBlock input").val(block[0].value); if(block.length > 1) { $(newRow).find(".segment-add-or").remove(); for(key = 1; key < block.length;key++) { var newSubRow = $(getMockedInputRowHtml()).clone(); $(newSubRow).find(".metricListBlock select").val(block[key].metric); $(newSubRow).find(".metricMatchBlock select").val(block[key].match); $(newSubRow).find(".metricValueBlock input").val(block[key].value); $(newRow).append(newSubRow).append(getOrDiv()); } $(newRow).append(getAddOrBlockButtonHtml()); } $(self.form).find(".segment-content").append(newRow).append(getAndDiv()); }; var applyInitialStateModification = function(){ $(self.form).find(".segment-add-row").remove(); $(self.form).find(".segment-content").append(getInitialStateRowsHtml()); doDragDropBindings(); }; var getSegmentFromId = function (id) { if(self.availableSegments.length > 0) { for(var i = 0; i < self.availableSegments.length; i++) { segment = self.availableSegments[i]; if(segment.idsegment == id) { return segment; } } } return false; }; var getListHtml = function() { var html = self.editorTemplate.find("> .listHtml").clone(); var segment, injClass; var listHtml = '<li data-idsegment="" ' + (self.currentSegmentStr == "" ? " class='segmentSelected' " : "") + ' data-definition=""><span class="segname">' + self.translations['SegmentEditor_DefaultAllVisits'] + ' ' + self.translations['General_DefaultAppended'] + '</span></li> '; if(self.availableSegments.length > 0) { for(var i = 0; i < self.availableSegments.length; i++) { segment = self.availableSegments[i]; injClass = ""; if( segment.definition == self.currentSegmentStr){ injClass = 'class="segmentSelected"'; } listHtml += '<li data-idsegment="'+segment.idsegment+'" data-definition="'+ (segment.definition).replace(/"/g, '"') +'" ' + injClass +' title="'+segment.name+'"><span class="segname">' + self.shortenSegmentName(segment.name)+'</span>'; if(self.segmentAccess == "write") { listHtml += '<span class="editSegment" title="'+ self.translations['General_Edit'].toLocaleLowerCase() +'"></span>'; } listHtml += '</li>'; } $(html).find(".segmentList > ul").append(listHtml); if(self.segmentAccess === "write"){ $(html).find(".add_new_segment").html(self.translations['SegmentEditor_AddNewSegment']); } else { $(html).find(".add_new_segment").hide(); } } else { $(html).find(".segmentList > ul").append(listHtml); } return html; }; var getFormHtml = function() { var html = self.editorTemplate.find("> .segment-element").clone(); // set left margin to center form var segmentsDropdown = $(html).find(".available_segments_select"); var segment, newOption; newOption = '<option data-idsegment="" data-definition="" title="' + self.translations['SegmentEditor_AddNewSegment'] + '">' + self.translations['SegmentEditor_AddNewSegment'] + '</option>'; segmentsDropdown.append(newOption); for(var i = 0; i < self.availableSegments.length; i++) { segment = self.availableSegments[i]; newOption = '<option data-idsegment="'+segment.idsegment+'" data-definition="'+(segment.definition).replace(/"/g, '"')+'" title="'+segment.name+'">'+self.shortenSegmentName(segment.name)+'</option>'; segmentsDropdown.append(newOption); } $(html).find(".segment-content > h3").after(getInitialStateRowsHtml()).show(); return html; }; var closeAllOpenLists = function() { $(".segmentationContainer", self.target).each(function() { if($(this).closest('.segmentEditorPanel').hasClass("visible")) $(this).trigger("click"); }); }; var findAndExplodeByMatch = function(metric){ var matches = ["==" , "!=" , "<=", ">=", "=@" , "!@","<",">"]; var newMetric = {}; var minPos = metric.length; var match, index; var singleChar = false; for(var key=0; key < matches.length; key++) { match = matches[key]; index = metric.indexOf(match); if( index != -1){ if(index < minPos){ minPos = index; if(match.length == 1){ singleChar = true; } } } } if(minPos < metric.length){ // sth found - explode if(singleChar == true){ newMetric.metric = metric.substr(0,minPos); newMetric.match = metric.substr(minPos,1); newMetric.value = metric.substr(minPos+1); } else { newMetric.metric = metric.substr(0,minPos); newMetric.match = metric.substr(minPos,2); newMetric.value = metric.substr(minPos+2); } // if value is only "" -> change to empty string if(newMetric.value == '""') { newMetric.value = ""; } } newMetric.value = decodeURIComponent(newMetric.value); return newMetric; }; var parseSegmentStr = function(segmentStr) { var blocks; blocks = segmentStr.split(";"); for(var key in blocks){ blocks[key] = blocks[key].split(","); for(var innerkey = 0; innerkey < blocks[key].length; innerkey++){ blocks[key][innerkey] = findAndExplodeByMatch(blocks[key][innerkey]); } } return blocks; }; var openEditForm = function(segment){ addForm("edit", segment); $(self.form).find(".segment-content > h3 > span").text(segment.name); $(self.form).find('.available_segments_select > option[data-idsegment="'+segment.idsegment+'"]').prop("selected",true); $(self.form).find('.available_segments a.dropList').text(self.shortenSegmentName(segment.name)); if(segment.definition != ""){ revokeInitialStateRows(); var blocks = parseSegmentStr(segment.definition); for(var key in blocks){ appendComplexRowHtml(blocks[key]); } $(self.form).find(".segment-content").append(getAddNewBlockButtonHtml()); } $(self.form).find(".metricList").each( function(){ $(this).trigger("change", true); }); doDragDropBindings(); }; var filterSegmentList = function (keyword) { var curTitle; clearFilterSegmentList(); $(self.target).find(" .filterNoResults").remove(); $(self.target).find(".segmentList li").each(function () { curTitle = $(this).prop('title'); $(this).hide(); if (curTitle.toLowerCase().indexOf(keyword.toLowerCase()) !== -1) { $(this).show(); } }); if ($(self.target).find(".segmentList li:visible").length == 0) { $(self.target).find(".segmentList li:first") .before("<li class=\"filterNoResults grayed\">" + self.translations['General_SearchNoResults'] + "</li>"); } } var clearFilterSegmentList = function () { $(self.target).find(" .filterNoResults").remove(); $(self.target).find(".segmentList li").each(function () { $(this).show(); }); } var bindEvents = function () { self.target.on('click', '.segmentationContainer', function (e) { // hide all other modals connected with this widget if (self.content.closest('.segmentEditorPanel').hasClass("visible")) { if ($(e.target).hasClass("jspDrag") === true || $(e.target).hasClass("segmentFilterContainer") === true || $(e.target).parents().hasClass("segmentFilterContainer") === true || $(e.target).hasClass("filterNoResults")) { e.stopPropagation(); } else { self.jscroll.destroy(); self.target.closest('.segmentEditorPanel').removeClass('visible'); } } else { // for each visible segmentationContainer -> trigger click event to close and kill scrollpane - very important ! closeAllOpenLists(); self.target.closest('.segmentEditorPanel').addClass('visible'); self.target.find('.segmentFilter').val(self.translations['General_Search']).trigger('keyup'); self.jscroll = self.target.find(".segmentList").jScrollPane({ autoReinitialise: true, showArrows:true }).data().jsp; } }); self.target.on('click', '.editSegment', function(e) { $(this).closest(".segmentationContainer").trigger("click"); var target = $(this).parent("li"); openEditFormGivenSegment(target); e.stopPropagation(); e.preventDefault(); }); self.target.on("click", ".segmentList li", function (e) { if ($(e.currentTarget).hasClass("grayed") !== true) { var segment = {}; segment.idsegment = $(this).attr("data-idsegment"); segment.definition = $(this).data("definition"); segment.name = $(this).attr("title"); self.setSegment(segment.definition); self.markCurrentSegment(); self.segmentSelectMethod( segment.definition ); toggleLoadingMessage(segment.definition.length); } }); self.target.on('click', '.add_new_segment', function (e) { e.stopPropagation(); closeAllOpenLists(); addForm("new"); doDragDropBindings(); }); self.target.on('change', "select.metricList", function (e, persist) { if (typeof persist === "undefined") { persist = false; } alterMatchesList(this, true); doDragDropBindings(); autoSuggestValues(this, persist); }); // attach event that will clear segment list filtering input after clicking x self.target.on('click', ".segmentFilterContainer span", function (e) { $(e.target).parent().find(".segmentFilter").val(self.translations['General_Search']).trigger('keyup'); }); self.target.on('blur', ".segmentFilter", function (e) { if ($(e.target).parent().find(".segmentFilter").val() == "") { $(e.target).parent().find(".segmentFilter").val(self.translations['General_Search']) } }); self.target.on('click', ".segmentFilter", function (e) { if ($(e.target).val() == self.translations['General_Search']) { $(e.target).val(""); } }); self.target.on('keyup', ".segmentFilter", function (e) { var search = $(e.currentTarget).val(); if (search == self.translations['General_Search']) { search = ""; } if (search.length >= 2) { clearTimeout(self.filterTimer); self.filterAllowed = true; self.filterTimer = setTimeout(function () { filterSegmentList(search); }, 500); } else { self.filterTimer = false; clearFilterSegmentList(); } }); // // segment editor form events // self.target.on('click', ".segment-element a:not(.crowdfundingLink)", function (e) { e.preventDefault(); }); self.target.on('click', "a.editSegmentName", function (e) { var oldName = $(e.currentTarget).parents("h3").find("span").text(); $(e.currentTarget).parents("h3").find("span").hide(); $(e.currentTarget).hide(); $(e.currentTarget).before('<input class="edit_segment_name" type="text"/>'); $(e.currentTarget).siblings(".edit_segment_name").focus().val(oldName); }); self.target.on("click", ".segmentName", function(e) { $(self.form).find("a.editSegmentName").trigger('click'); }); self.target.on('blur', "input.edit_segment_name", function (e) { var newName = $(this).val(); var segmentNameNode = $(e.currentTarget).parents("h3").find("span"); if(newName.trim()) { segmentNameNode.text(newName); } else { $(this).val(segmentNameNode.text()); } }); self.target.on('click', '.segment-element', function (e) { e.stopPropagation(); e.preventDefault(); }); self.target.on('change', '.available_segments_select', function (e) { var option = $(e.currentTarget).find('option:selected'); openEditFormGivenSegment(option); }); // attach event that shows/hides child elements of each metric category self.target.on('click', '.segment-nav a.metric_category', function (e) { $(e.currentTarget).siblings("ul").toggle(); }); self.target.on('click', ".custom_select_search a", function (e) { $(self.form).find(".segmentSearch").val("").trigger("keyup").val(self.translations['General_Search']); }); // attach event that will clear search input upon focus if its content is default self.target.on('focus', '.segmentSearch', function (e) { var search = $(e.currentTarget).val(); if(search == self.translations['General_Search']) $(e.currentTarget).val(""); }); // attach event that will set search input value upon blur if its content is not null self.target.on('blur', '.segmentSearch', function (e) { var search = $(e.currentTarget).val(); if(search == ""){ clearSearchMetricHighlight(); $(e.currentTarget).val(self.translations['General_Search']); } }); // bind search action triggering - only when input text is longer than 2 chars self.target.on('keyup', '.segmentSearch', function (e) { var search = $(e.currentTarget).val(); if( search.length >= 2) { clearTimeout(self.timer); self.searchAllowed = true; self.timer = setTimeout(function(){ searchSegments(search); }, 500); } else{ self.searchAllowed = false; clearSearchMetricHighlight(); } }); self.target.on('click', ".delete", function() { var segmentName = $(self.form).find(".segment-content > h3 > span").text(); var segmentId = $(self.form).find(".available_segments_select option:selected").attr("data-idsegment"); var params = { "idsegment" : segmentId }; $('.segment-delete-confirm', self.target).find('#name').text( segmentName ); if(segmentId != ""){ piwikHelper.modalConfirm(self.target.find('.segment-delete-confirm'), { yes: function(){ self.deleteMethod(params); } }); } }); self.target.on("click", "a.close", function (e) { $(".segmentListContainer", self.target).show(); closeForm(); }); $("body").on("keyup", function (e) { if(e.keyCode == "27"){ $(".segmentListContainer", self.target).show(); closeForm(); } }); // // segment manipulation events // // upon clicking - add new segment block, then bind 'x' action to newly added row self.target.on('click', ".segment-add-row a", function(event, data){ var mockedRow = getMockedFormRow(); $(self.form).find(".segment-and:last").after(getAndDiv()).after(mockedRow); if(typeof data !== "undefined"){ $(self.form).find(".metricList:last").val(data); } $(self.form).find(".metricList:last").trigger('change'); preselectFirstMetricMatch(mockedRow); doDragDropBindings(); }); self.target.on("click", ".segment-add-row span", function(event, data){ if(typeof data !== "undefined") { var mockedRow = getMockedFormRow(); $(self.form).find(".segment-and:last").after(getAndDiv()).after(mockedRow); preselectFirstMetricMatch(mockedRow); $(self.form).find(".metricList:last").val(data).trigger('change'); doDragDropBindings(); } }); // add new OR block self.target.on("click", ".segment-add-or a", function(event, data){ var parentRows = $(event.currentTarget).parents(".segment-rows"); parentRows.find(".segment-or:last").after(getOrDiv()).after(getMockedInputRowHtml()); if(typeof data !== "undefined"){ parentRows.find(".metricList:last").val(data); } parentRows.find(".metricList:last").trigger('change'); var addedRow = parentRows.find('.segment-row:last'); preselectFirstMetricMatch(addedRow); doDragDropBindings(); }); self.target.on("click", ".segment-close", function (e) { var target = e.currentTarget; var rowCnt = $(target).parents(".segment-rows").find(".segment-row").length; var globalRowCnt = $(self.form).find(".segment-close").length; if(rowCnt > 1){ $(target).parents(".segment-row").next().remove(); $(target).parents(".segment-row").remove(); } else if(rowCnt == 1){ $(target).parents(".segment-rows").next().remove(); $(target).parents(".segment-rows").remove(); if(globalRowCnt == 1){ applyInitialStateModification(); } } }); }; // Request auto-suggest values var autoSuggestValues = function(select, persist) { var type = $(select).find("option:selected").attr("value"); if(!persist) { var parents = $(select).parents('.segment-row'); var loadingElement = parents.find(".segment-loading"); loadingElement.show(); var inputElement = parents.find(".metricValueBlock input"); var segmentName = $('option:selected',select).attr('value'); var ajaxHandler = new ajaxHelper(); ajaxHandler.addParams({ module: 'API', format: 'json', method: 'API.getSuggestedValuesForSegment', segmentName: segmentName }, 'GET'); ajaxHandler.useRegularCallbackInCaseOfError = true; ajaxHandler.setTimeout(20000); ajaxHandler.setErrorCallback(function(response) { loadingElement.hide(); inputElement.autocomplete({ source: [], minLength: 0 }); $(inputElement).autocomplete('search', $(inputElement).val()); }); ajaxHandler.setCallback(function(response) { loadingElement.hide(); if (response && response.result != 'error') { inputElement.autocomplete({ source: response, minLength: 0, select: function(event, ui){ event.preventDefault(); $(inputElement).val(ui.item.value); } }); } inputElement.click(function (e) { $(inputElement).autocomplete('search', $(inputElement).val()); }); }); ajaxHandler.send(); } }; var alterMatchesList = function(select, persist){ var oldMatch; var type = $(select).find("option:selected").attr("data-type"); var matchSelector = $(select).parents(".segment-input").siblings(".metricMatchBlock").find("select"); if(persist === true){ oldMatch = matchSelector.find("option:selected").val(); } else { oldMatch = ""; } if(type === "dimension" || type === "metric"){ matchSelector.empty(); var optionsHtml = ""; for(var key in self.availableMatches[type]){ optionsHtml += '<option value="'+key+'">'+self.availableMatches[type][key]+'</option>'; } } matchSelector.append(optionsHtml); if (matchSelector.find('option[value="' + oldMatch + '"]').length) { matchSelector.val(oldMatch); } else { preselectFirstMetricMatch(matchSelector.parent()); } }; var getAddNewBlockButtonHtml = function() { if(typeof addNewBlockButton === "undefined") { var addNewBlockButton = self.editorTemplate.find("> div.segment-add-row").clone(); } return addNewBlockButton.clone(); }; var getAddOrBlockButtonHtml = function(){ if(typeof addOrBlockButton === "undefined") { var addOrBlockButton = self.editorTemplate.find("div.segment-add-or").clone(); } return addOrBlockButton.clone(); }; var placeSegmentationFormControls = function(){ doDragDropBindings(); $(self.form).find(".scrollable").jScrollPane({ showArrows: true, autoReinitialise: true, verticalArrowPositions: 'os', horizontalArrowPositions: 'os' }); }; function openEditFormGivenSegment(option) { var segment = {}; segment.idsegment = option.attr("data-idsegment"); var segmentExtra = getSegmentFromId(segment.idsegment); for(var item in segmentExtra) { segment[item] = segmentExtra[item]; } segment.name = option.attr("title"); segment.definition = option.data("definition"); openEditForm(segment); } var doDragDropBindings = function(){ $(self.form).find(".segment-nav div > ul > li > ul > li").sortable({ cursor: 'move', revert: 10, revertDuration: 0, snap: false, helper: 'clone', appendTo: 'body' }); $(self.form).find(".metricListBlock").droppable({ hoverClass: "hovered", drop: function( event, ui ) { $(this).find("select").val(ui.draggable.parent().attr("data-metric")).trigger("change"); } }); $(self.form).find(".segment-add-row > div").droppable({ hoverClass: "hovered", drop: function( event, ui ) { $(this).find("a").trigger("click", [ui.draggable.parent().attr("data-metric")]); if($(this).find("a > span").length == 0){ revokeInitialStateRows(); appendSpecifiedRowHtml([ui.draggable.parent().attr("data-metric")]); } } }); $(self.form).find(".segment-add-or > div").droppable({ hoverClass: "hovered", drop: function( event, ui ) { $(this).find("a").trigger("click", [ui.draggable.parent().attr("data-metric")]); } }); }; var searchSegments = function(search){ // pre-process search string to normalized form search = normalizeSearchString(search); // --- // clear all previous search highlights and hide all categories // to allow further showing only matching ones, while others remain invisible clearSearchMetricHighlight(); $(self.form).find('.segment-nav div > ul > li').hide(); var curStr = ""; var curMetric = ""; // 1 - do most obvious selection -> mark whole categories matching search string // also expand whole category $(self.form).find('.segment-nav div > ul > li').each( function(){ curStr = normalizeSearchString($(this).find("a.metric_category").text()); if(curStr.indexOf(search) > -1) { $(this).addClass("searchFound"); $(this).find("ul").show(); $(this).find("li").show(); $(this).show(); } } ); // 2 - among all unselected categories find metrics which match and mark parent as search result $(self.form).find(".segment-nav div > ul > li:not(.searchFound)").each(function(){ var parent = this; $(this).find("li").each( function(){ var curStr = normalizeSearchString($(this).text()); var curMetric = normalizeSearchString($(this).attr("data-metric")); $(this).hide(); if(curStr.indexOf(search) > -1 || curMetric.indexOf(search) > -1){ $(this).show(); $(parent).find("ul").show(); $(parent).addClass("searchFound").show(); } }); }); if( $(self.form).find("li.searchFound").length == 0) { $(self.form).find("div > ul").prepend('<li class="no_results"><a>'+self.translations['General_SearchNoResults']+'</a></li>').show(); } // check if search allow flag was revoked - then clear all search results if(self.searchAllowed == false) { clearSearchMetricHighlight(); self.searchAllowed = true; } }; var clearSearchMetricHighlight = function(){ $(self.form).find('.no_results').remove(); $(self.form).find('.segment-nav div > ul > li').removeClass("searchFound").show(); $(self.form).find('.segment-nav div > ul > li').removeClass("others").show(); $(self.form).find('.segment-nav div > ul > li > ul > li').show(); $(self.form).find('.segment-nav div > ul > li > ul').hide(); }; var normalizeSearchString = function(search){ search = search.replace(/^\s+|\s+$/g, ''); // trim search = search.toLowerCase(); // remove accents, swap ñ for n, etc var from = "àáäâèéëêìíïîòóöôùúüûñç·/_,:;"; var to = "aaaaeeeeiiiioooouuuunc------"; for (var i=0, l=from.length ; i<l ; i++) { search = search.replace(new RegExp(from.charAt(i), 'g'), to.charAt(i)); } search = search.replace(/[^a-z0-9 -]/g, '') // remove invalid chars .replace(/\s+/g, '_') // collapse whitespace and replace by underscore .replace(/-+/g, '-'); // collapse dashes return search; }; // Mode = 'new' or 'edit' var addForm = function(mode, segment){ self.target.find(".segment-element:visible").unbind().remove(); if (typeof self.form !== "undefined") { closeForm(); } // remove any remaining forms self.form = getFormHtml(); self.target.prepend(self.form); // if there's enough space to the left & not enough space to the right, // anchor the form to the right of the selector if (self.form.width() + self.target.offset().left > $(window).width() && self.form.width() < self.target.offset().left + self.target.width() ) { self.form.addClass('anchorRight'); } placeSegmentationFormControls(); if(mode == "edit") { $(self.form).find('.enable_all_users_select > option[value="'+segment.enable_all_users+'"]').prop("selected",true); $(self.form).find('.visible_to_website_select > option[value="'+segment.enable_only_idsite+'"]').prop("selected",true); $(self.form).find('.auto_archive_select > option[value="'+segment.auto_archive+'"]').prop("selected",true); } makeDropList(".enable_all_users" , ".enable_all_users_select"); makeDropList(".visible_to_website" , ".visible_to_website_select"); makeDropList(".auto_archive" , ".auto_archive_select"); makeDropList(".available_segments" , ".available_segments_select"); $(self.form).find(".saveAndApply").bind("click", function (e) { e.preventDefault(); parseFormAndSave(); }); $(self.form).find('.segment-footer').hover( function() { $('.segmentFooterNote', self.target).fadeIn(); }, function() { $('.segmentFooterNote', self.target).fadeOut(); }); if(typeof mode !== "undefined" && mode == "new") { $(self.form).find(".editSegmentName").trigger('click'); } $(".segmentListContainer", self.target).hide(); self.target.closest('.segmentEditorPanel').addClass('editing'); piwikHelper.compileAngularComponents(self.target); }; var closeForm = function () { $(self.form).unbind().remove(); self.target.closest('.segmentEditorPanel').removeClass('editing'); }; var parseForm = function(){ var segmentStr = ""; $(self.form).find(".segment-rows").each( function(){ var subSegmentStr = ""; $(this).find(".segment-row").each( function(){ if(subSegmentStr != ""){ subSegmentStr += ","; // OR operator } $(this).find(".segment-row-inputs").each( function(){ var metric = $(this).find(".metricList option:selected").val(); var match = $(this).find(".metricMatchBlock > select option:selected").val(); var value = $(this).find(".segment-input input").val(); subSegmentStr += metric + match + encodeURIComponent(value); }); }); if(segmentStr != "") { segmentStr += ";"; // add AND operator between segment blocks } segmentStr += subSegmentStr; }); return segmentStr }; var parseFormAndSave = function(){ var segmentName = $(self.form).find(".segment-content > h3 >span").text(); var segmentStr = parseForm(); var segmentId = $(self.form).find('.available_segments_select > option:selected').attr("data-idsegment"); var user = $(self.form).find(".enable_all_users_select option:selected").val(); var autoArchive = $(self.form).find(".auto_archive_select option:selected").val() || 0; var params = { "name": segmentName, "definition": segmentStr, "enabledAllUsers": user, "autoArchive": autoArchive, "idSite": $(self.form).find(".visible_to_website_select option:selected").val() }; // determine if save or update should be performed if(segmentId === ""){ self.addMethod(params); } else{ jQuery.extend(params, { "idSegment": segmentId }); self.updateMethod(params); } }; var makeDropList = function(spanId, selectId){ var select = $(self.form).find(selectId).hide(); var dropList = $( '<a class="dropList dropdown">' ) .insertAfter( select ) .text( select.children(':selected').text() ) .autocomplete({ delay: 0, minLength: 0, appendTo: "body", source: function( request, response ) { response( select.children( "option" ).map(function() { var text = $( this ).text(); return { label: text, value: this.value, option: this }; }) ); }, select: function( event, ui ) { event.preventDefault(); ui.item.option.selected = true; // Mark original select>option $(spanId + ' option[value="' + ui.item.value + '"]', self.editorTemplate).prop('selected', true); dropList.text(ui.item.label); $(self.form).find(selectId).trigger("change"); } }) .click(function() { // close all other droplists made by this form $("a.dropList").autocomplete("close"); // close if already visible if ( $(this).autocomplete( "widget" ).is(":visible") ) { $(this).autocomplete("close"); return; } // pass empty string as value to search for, displaying all results $(this).autocomplete( "search", "" ); }); $('body').on('mouseup',function (e) { if (!$(e.target).parents(spanId).length && !$(e.target).is(spanId) && !$(e.target).parents(spanId).length && !$(e.target).parents(".ui-autocomplete").length && !$(e.target).is(".ui-autocomplete") && !$(e.target).parents(".ui-autocomplete").length ) { dropList.autocomplete().autocomplete("close"); } }); }; function toggleLoadingMessage(segmentIsSet) { if (segmentIsSet) { $('#ajaxLoadingDiv').find('.loadingSegment').show(); } else { $('#ajaxLoadingDiv').find('.loadingSegment').hide(); } } this.initHtml = function() { var html = getListHtml(); if(typeof self.content !== "undefined"){ this.content.html($(html).html()); } else { this.target.append(html); this.content = this.target.find(".segmentationContainer"); } // assign content to object attribute to make it easil accesible through all widget methods this.markCurrentSegment(); // Loading message var segmentIsSet = this.getSegment().length; toggleLoadingMessage(segmentIsSet); }; this.initHtml(); bindEvents(); }; return segmentation; })(jQuery); $(document).ready(function() { var exports = require('piwik/UI'); var UIControl = exports.UIControl; /** * Sets up and handles events for the segment selector & editor control. * * @param {Element} element The HTML element generated by the SegmentSelectorControl PHP class. Should * have the CSS class 'segmentEditorPanel'. * @constructor */ var SegmentSelectorControl = function (element) { UIControl.call(this, element); if ((typeof this.props.isSegmentNotAppliedBecauseBrowserArchivingIsDisabled != "undefined") && this.props.isSegmentNotAppliedBecauseBrowserArchivingIsDisabled ) { piwikHelper.modalConfirm($('.pleaseChangeBrowserAchivingDisabledSetting', this.$element), { yes: function () {} }); } var self = this; this.changeSegment = function(segmentDefinition) { segmentDefinition = cleanupSegmentDefinition(segmentDefinition); segmentDefinition = encodeURIComponent(segmentDefinition); return broadcast.propagateNewPage('segment=' + segmentDefinition, true); }; this.changeSegmentList = function () {}; var cleanupSegmentDefinition = function(definition) { definition = definition.replace("'", "%29"); definition = definition.replace("&", "%26"); return definition; }; var addSegment = function(params){ var ajaxHandler = new ajaxHelper(); ajaxHandler.setLoadingElement(); params.definition = cleanupSegmentDefinition(params.definition); ajaxHandler.addParams($.extend({}, params, { "module": 'API', "format": 'json', "method": 'SegmentEditor.add' }), 'GET'); ajaxHandler.useCallbackInCaseOfError(); ajaxHandler.setCallback(function (response) { if (response && response.result == 'error') { alert(response.message); } else { params.idsegment = response.value; self.props.availableSegments.push(params); self.rebuild(); self.impl.setSegment(params.definition); self.impl.markCurrentSegment(); self.$element.find('a.close').click(); self.changeSegment(params.definition); self.changeSegmentList(self.props.availableSegments); } }); ajaxHandler.send(true); }; var updateSegment = function(params){ var ajaxHandler = new ajaxHelper(); ajaxHandler.setLoadingElement(); params.definition = cleanupSegmentDefinition(params.definition); ajaxHandler.addParams($.extend({}, params, { "module": 'API', "format": 'json', "method": 'SegmentEditor.update' }), 'GET'); ajaxHandler.useCallbackInCaseOfError(); ajaxHandler.setCallback(function (response) { if (response && response.result == 'error') { alert(response.message); } else { params.idsegment = params.idSegment; var idx = null; for (idx in self.props.availableSegments) { if (self.props.availableSegments[idx].idsegment == params.idSegment) { break; } } self.props.availableSegments[idx] = params; self.rebuild(); self.impl.setSegment(params.definition); self.impl.markCurrentSegment(); self.$element.find('a.close').click(); self.changeSegment(params.definition); self.changeSegmentList(self.props.availableSegments); } }); ajaxHandler.send(true); }; var deleteSegment = function(params){ var ajaxHandler = new ajaxHelper(); ajaxHandler.addParams({ module: 'API', format: 'json', method: 'SegmentEditor.delete' }, 'GET'); ajaxHandler.addParams({ idSegment: params.idsegment }, 'POST'); ajaxHandler.setLoadingElement(); ajaxHandler.useCallbackInCaseOfError(); ajaxHandler.setCallback(function (response) { if (response && response.result == 'error') { alert(response.message); } else { self.impl.setSegment(''); self.impl.markCurrentSegment(); var idx = null; for (idx in self.props.availableSegments) { if (self.props.availableSegments[idx].idsegment == params.idsegment) { break; } } self.props.availableSegments.splice(idx, 1); self.rebuild(); self.$element.find('a.close').click(); self.changeSegment(''); $('.ui-dialog-content').dialog('close'); self.changeSegmentList(self.props.availableSegments); } }); ajaxHandler.send(true); }; var segmentFromRequest = encodeURIComponent(self.props.selectedSegment) || broadcast.getValueFromHash('segment') || broadcast.getValueFromUrl('segment'); if($.browser.mozilla) { segmentFromRequest = decodeURIComponent(segmentFromRequest); } var userSegmentAccess = (this.props.authorizedToCreateSegments) ? "write" : "read"; this.impl = new Segmentation({ "target" : this.$element.find(".segmentListContainer"), "editorTemplate": $('.SegmentEditor', self.$element), "segmentAccess" : userSegmentAccess, "availableSegments" : this.props.availableSegments, "addMethod": addSegment, "updateMethod": updateSegment, "deleteMethod": deleteSegment, "segmentSelectMethod": function () { self.changeSegment.apply(this, arguments); }, "currentSegmentStr": segmentFromRequest, "translations": this.props.segmentTranslations }); this.onMouseUp = function(e) { if ($(e.target).closest('.segment-element').length === 0 && !$(e.target).is('.segment-element') && $(e.target).hasClass("ui-corner-all") == false && $(e.target).hasClass("ddmetric") == false && $(".segment-element:visible", self.$element).length == 1 ) { $(".segment-element:visible a.close", self.$element).click(); } if ($(e.target).closest('.segmentListContainer').length === 0 && self.$element.hasClass("visible") ) { $(".segmentationContainer", self.$element).trigger("click"); } }; $('body').on('mouseup', this.onMouseUp); // re-initialize top controls since the size of the control is not the same after it's // initialized. initTopControls(); }; /** * Initializes all elements w/ the .segmentEditorPanel CSS class as SegmentSelectorControls, * if the element has not already been initialized. */ SegmentSelectorControl.initElements = function () { UIControl.initElements(this, '.segmentEditorPanel'); }; $.extend(SegmentSelectorControl.prototype, UIControl.prototype, { getSegment: function () { return this.impl.getSegment(); }, setSegment: function (segment) { return this.impl.setSegment(segment); }, rebuild: function () { this.impl.setAvailableSegments(this.props.availableSegments); this.impl.initHtml(); }, _destroy: function () { UIControl.prototype._destroy.call(this); $('body').off('mouseup', null, this.onMouseUp); } }); exports.SegmentSelectorControl = SegmentSelectorControl; });